#pragma once

#include <iostream>
#include <string>
#include <time.h>
#include <memory>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <unistd.h>
using namespace std;

#define GET_SEM (IPC_CREAT | IPC_EXCL | 0666)
#define USE_SEM IPC_CREAT
const string pathname = ".";
const int proj_id = 0x88;
const int _nums = 1;

class Sem
{
public:
    Sem(int semid, int flag)
        : _semid(semid), _flag(flag)
    {
    }
    void P()
    {
        PV(-1);
    }
    void V()
    {
        PV(1);
    }
    ~Sem()
    {
        if (_flag == GET_SEM)
        {
            int n = semctl(_semid, 0, IPC_RMID);
            if (n == -1)
            {
                cerr << "fail remove!" << endl;
            }
            else
            {
                cout << "success remove" << endl;
            }
        }
    }

private:
    void PV(int data)
    {
        struct sembuf sb;
        sb.sem_flg = SEM_UNDO;
        sb.sem_num = 0;
        sb.sem_op = data;
        int n = semop(_semid,&sb,1);
        if(n == -1)
        {
            cerr<<"fail PV"<<endl;
            exit(4);
        }
    }

private:
    int _semid;
    int _flag;
};

class SemBuilder
{
public:
    SemBuilder()
        : _val(-1)
    {
    }
    SemBuilder &SET_VAL(int val)
    {
        _val = val;
        return *this;
    }
    shared_ptr<Sem> Build(int flag)
    {
        if (_val < 0)
        {
            cout << "you must SETVAL first" << endl;
            return nullptr;
        }

        key_t k = ftok(pathname.c_str(), proj_id);
        if (k == -1)
        {
            cerr << "ftok error" << endl;
            exit(1);
        }
        cout << "ftok success!" << endl;

        int n = semget(k, 1, flag);
        if (n == -1)
        {
            cerr << "semget error" << endl;
            exit(2);
        }
        cout << "semget success!" << endl;

        if (flag == GET_SEM)
        {
            union Semun
            {
                int val;
                struct semid_ds *buf;
                unsigned short *array;
                struct seminfo *__buf;
            } un;
            un.val = _val;
            int x = semctl(n, 0, SETVAL, un);
            if (x < 0)
            {
                cerr << "semctl SETVAL error" << endl;
                exit(3);
            }
            cout << "SETVAL success" << endl;
        }
        return make_shared<Sem>(n, flag);
    }
    ~SemBuilder()
    {
    }

private:
    int _val;
};